package com.atguigu.gulimall.seckill.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class MyRabbitConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制rabbitTemplate
     * @PostConstruct对象创建完成以后,执行这个方法
     * 1: 服务受到消息就回调,步骤:配置文件spring.rabbitmq.publisher-confirms=true;设置确认回调confirmCallback
     * 2: 消息正确抵达队列进行回调,配置文件:
     *     spring.rabbitmq.publisher-returns=true
     *     spring.rabbitmq.template.mandatory=true
     *     设置确认回调ReturnCallback
     * 3: 消费端确认(保证每个消息被正确消费,此时才可以broker删除这个消息)
     *    1:rabbitMQ默认是自动确认的,只要消息受到,客户端会自动确认,服务端就会移除这个消息
     *          产生的问题:我们收到多个消息,自动回复给服务器ack,但是只有一个消息消费了,服务就宕机了,发生消息丢失
     *          解决方案:消费端手动确认ACK模式,只要我们没有手动设置ack告诉MQ消息受到了,消息就一个处于unAck状态保存在队列中,即使服务器宕机了,消息也不会丢失
     *          
     **/
    @PostConstruct
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * correlationData:当前消息的唯一关联数据(消息的唯一ID)
             * ack:消息是否成功收到
             * cause:消息发送失败的原因
             **/
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                /**
                 * 做好消息确认机制,保证发送者和消费者都是手动确认机制
                 * 每一个消息,都在数据库做好记录,定期将失败的消息重新处理
                 **/
            }
        });

        //设置消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递给指定队列,就触发这个失败的回调方法
             * message:投递失败的详细信息
             * replyCode:恢复的状态码
             * replyText:回复的文本内容
             * exchange:当时发送消息的给哪个的交换机
             * routingKey:当时发送消息的给哪个路邮件
             **/
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {

            }
        });
    }

}
